package controller;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.GregorianCalendar;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import model.Benutzer;
import model.Ebene;
import model.Feld;
import model.Gruppe;

/**
 * Dieses Servlet zeigt den Baum der zur Navigation durch die Ebenen verwedet
 * wird. Der Administrator kann in diesen Baum auch neue Ebenen einfügen.
 */
@WebServlet("/Baum.json")
public class Baum extends SessionControll {
        private static final long serialVersionUID = 1L;

        /**
         * @see HttpServlet#HttpServlet()
         */
        public Baum() {
                super();
        }

        /**
         * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
         *      response)
         */
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                request.setCharacterEncoding("UTF-8");
                Ebene startEbene = ebenenRepository.getNeusteEbeneNachId(1);

                String baum = "";
                Ebene[] temp;
                try {
                        temp = startEbene.getUnterelement();
                        if (temp != null) {
                                baum += "[";
                                for (int i = 0; i < temp.length; i++) {
                                        baum += getEbene(temp[i]);
                                }
                                baum += "]";
                        }
                        baum = baum.replace("}{", "},{");
                } catch (ClassNotFoundException e) {
                	new helper.Logging("Fehler F010",e.toString(), benutzerRepository.getBenutzerNachEmail((String) request.getSession().getAttribute("email")), (byte)1);    
                } catch (SQLException e) {
                	new helper.Logging("Fehler F011",e.toString(), benutzerRepository.getBenutzerNachEmail((String) request.getSession().getAttribute("email")), (byte)1);
                }

                request.setAttribute("baum", baum);

                RequestDispatcher view;
                view = request.getRequestDispatcher("Baum.jsp");
                view.forward(request, response);

        }

        private String getEbene(Ebene ebene) throws ClassNotFoundException,
                        SQLException {

                String output = "{\"title\": \"" + ebene.getName()
                                + "\", \"href\":\"Vorschau?ebenenId=" + ebene.getId()
                                + "\" , \"addClass\": \"id" + ebene.getId()
                				+ "\"";
                Ebene[] temp = ebene.getUnterelement();
                if (temp != null) {
                        output += ", \"children\": [";
                        for (int i = 0; i < temp.length; i++) {
                                output += getEbene(temp[i]);
                        }
                        output += "]";
                }

                output += "}";
                output = output.replace("}{", "},{");
                return output;

        }

        /**
         * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
         *      response)
         */
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
                request.setCharacterEncoding("UTF-8");
                if (super.gruppePruefen(request, response, Gruppe.ADMIN)) {
                        String newBaum = request.getParameter("newBaum");
                        System.out.println(newBaum);
                        if (newBaum != null) {
                                // --- BAUM PARSEN ---
                                System.out.println(newBaum);
                                newBaum = newBaum.substring(1, newBaum.length() - 1); // die äußersten geschweiften Klammern entfernen
                                ArrayList<TreeNode<Integer>> kinder = new ArrayList<TreeNode<Integer>>();
                                int start, c;
                                for (int pos = 0; pos < newBaum.length();) { // Alle Kinder durchgehen
                                        start = -1;
                                        c = 0;
                                        while (c == 0 && pos < newBaum.length()) { // Start finden
                                                if (newBaum.charAt(pos) == '{') {
                                                        c++;
                                                        start = pos;
                                                }
                                                pos++;
                                        }
                                        do { // Ende finden
                                                if (newBaum.charAt(pos) == '{') {
                                                        c++;
                                                } else if (newBaum.charAt(pos) == '}') {
                                                        c--;
                                                }
                                                pos++;
                                        } while (pos < newBaum.length() && c > 0);
                                        if (start != -1 && c == 0) { // Wenn Start und Ende gefunden, dann Substring und weitere rekursion
                                                kinder.add(parseBaumElement(newBaum.substring(start, pos)));
                                        }
                                }
                               
                                // übergebenen Baum auf Rekursion prüfen
                                ArrayList<Integer> eltern = new ArrayList<Integer>();
                                eltern.add(new Integer(1));
                                boolean endlosRekursion = false;
                                for (int i=0; i<kinder.size(); i++) {
                                        if(rekursionVorhanden(kinder.get(i), eltern)) {
                                                endlosRekursion = true;
                                                break;
                                        }
                                }
                               
                                // --- BAUM DATENBANK PRÜFEN ---
                                try {
                                        if(endlosRekursion) {
                                                throw new IllegalArgumentException("Baum enthält Elemente die sich selbst als Unterelement haben.");
                                        }
                                        Ebene root = ebenenRepository.getNeusteEbeneNachId(1); // Ebenenobjekt holen
                                        ArrayList<Ebene> geaenderteEbenen = new ArrayList<Ebene>();
                                        for (int i = 0; i < kinder.size(); i++) { // Kinder Updaten
                                                baumUpdateEbene(kinder.get(i), request, geaenderteEbenen);
                                        }
                                        // Neue Ebenenversionen speichern (nur wenn die Ebene nicht zweimal geändert wurde (verschieben von unterelement in Überelement an anderer Stelle im Baum (Hardlinks)))
                                        for(int i=0; i<geaenderteEbenen.size(); i++) {
                                                for(int j=i+1; j<geaenderteEbenen.size(); j++) {
                                                        if(geaenderteEbenen.get(i).getId() == geaenderteEbenen.get(j).getId()) {
                                                                throw new IllegalArgumentException("Versuchtes Verschieben von einem Unterelement in das Überelement an anderer Stelle im Baum (Hardlinks).");
                                                        }
                                                }
                                                ebenenRepository.saveEbene(geaenderteEbenen.get(i));
                                        }
                                       
                                        Ebene[] unterelemente = root.getUnterelement();
                                        boolean update = false;
                                        if (unterelemente != null) {
                                                System.out.println("unterelemente != null");
                                                if (kinder.size() != unterelemente.length) { // gucken ob Update nötig
                                                        System.out.println("kinder.size() != unterelemente.length");
                                                        update = true;
                                                } else {
                                                        for (int i = 0; i < unterelemente.length; i++) { // gucken ob Update nötig
                                                                if (kinder.get(i).wert != unterelemente[i].getId()) {
                                                                        System.out.println("kinder.get(i).wert != unterelemente[i].getId()");
                                                                        update = true;
                                                                        break;
                                                                }
                                                        }
                                                }
                                        } else if (kinder.size() > 0) { // gucken ob Update nötig
                                                update = true;
                                        }
                                        if (update) {
                                                // Update
                                                String idsRedakt = "", idsVerant = "", idsUnter = "", idsFelder = "";
                                                System.out.println("kinder.size():" + kinder.size());
                                                for (int i = 0; i < kinder.size(); i++) {
                                                        idsUnter += kinder.get(i).wert + ";";
                                                }
                                                System.out.println("idsUnter" + idsUnter);
                                                if (idsUnter.length() > 1) {
                                                        idsUnter.substring(0, idsUnter.length() - 1);
                                                }
                                                if (root.getVerantwortlich() != null) {
                                                        for (int i = 0; i < root.getVerantwortlich().length; i++) {
                                                                idsVerant += root.getVerantwortlich()[i] + ";";
                                                        }
                                                }
                                                if (idsVerant.length() > 1) {
                                                        idsVerant.substring(0, idsVerant.length() - 1);
                                                }
                                                if (root.getRedakteur() != null) {
                                                        for (int i = 0; i < root.getRedakteur().length; i++) {
                                                                idsRedakt += root.getRedakteur()[i] + ";";
                                                        }
                                                }
                                                if (idsRedakt.length() > 1) {
                                                        idsRedakt.substring(0, idsRedakt.length() - 1);
                                                }
                                                if (root.getFeld() != null) {
                                                        for (int i = 0; i < root.getFeld().length; i++) {
                                                                idsFelder += root.getFeld()[i] + ";";
                                                        }
                                                }
                                                if (idsFelder.length() > 1) {
                                                        idsFelder.substring(0, idsFelder.length() - 1);
                                                }
                                                ebenenRepository.saveEbene(root = new Ebene(root.getId(),
				                                                                            root.getName(),
				                                                                            root.getProsaId(),
				                                                                            (new GregorianCalendar()).getTimeInMillis(),
				                                                                            root.getRedaktionszustand(),
				                                                                            idsVerant,
				                                                                            idsRedakt,
				                                                                            idsUnter,
				                                                                            idsFelder));
                                        }
                                } catch (SQLException e) {
                                	new helper.Logging("Fehler F012",e.toString(), benutzerRepository.getBenutzerNachEmail((String) request.getSession().getAttribute("email")), (byte)1);
                                        e.printStackTrace();
                                } catch (ClassNotFoundException e) {
                                	new helper.Logging("Fehler F013",e.toString(), benutzerRepository.getBenutzerNachEmail((String) request.getSession().getAttribute("email")), (byte)1);
                                        e.printStackTrace();
                                        response.sendRedirect("Fehler?weiterleitung=Index.htm&fehler=Es konnte nicht auf die Datenbank zugegriffen werden! Bitte versuchen Sie es erneut oder kontaktieren Sie einen Administrator. (F013)");
                                } catch (IllegalArgumentException e) {
                                	new helper.Logging("Fehler F014",e.toString(), benutzerRepository.getBenutzerNachEmail((String) request.getSession().getAttribute("email")), (byte)1);
                                        request.setAttribute("baum", e.getMessage());
                                }
                        }
                }
                RequestDispatcher view;
                view = request.getRequestDispatcher("Baum.jsp");
                view.forward(request, response);
        }

        private void baumUpdateEbene(TreeNode<Integer> knoten, HttpServletRequest request, ArrayList<Ebene> geaenderteEbenen) throws ClassNotFoundException, SQLException, IllegalArgumentException {
                if (knoten.wert == -1) { // neue Ebene anlegen
                        knoten.wert = ebenenRepository.newEbene("Neues Element", 0,
                                        Ebene.ZUSTAND_UNBEARBEITET, new Benutzer[0], new Benutzer[0],
                                        new Ebene[0], new Feld[0]).getId();
                        request.setAttribute("baum", ""+knoten.wert);
                } else { // nach Update gucken
                        for (int i = 0; i < knoten.kinder.size(); i++) { // Kinder updaten
                                baumUpdateEbene(knoten.kinder.get(i), request, geaenderteEbenen);
                        }
                        Ebene ebene = ebenenRepository.getNeusteEbeneNachId(knoten.wert); // Ebenenobjekt holen
                        if (ebene == null) {
                                throw new IllegalArgumentException("Fehler: ID aus Baum gehört zu keiner existierenden Ebene. (F014)");
                        }
                        boolean update = false;
                        Ebene[] unterelemente = ebenenRepository.getNeusteEbeneNachId(knoten.wert).getUnterelement();
                        if (unterelemente != null) {
                                if (knoten.kinder.size() != unterelemente.length) { // gucken ob Update nötig
                                        update = true;
                                } else {
                                        for (int i = 0; i < unterelemente.length; i++) { // gucken ob Update nötig
                                                if (knoten.kinder.get(i).wert != unterelemente[i].getId()) {
                                                        update = true;
                                                        break;
                                                }
                                        }
                                }
                        } else if (knoten.kinder.size() > 0) { // gucken ob Update nötig
                                update = true;
                        }
                        if (update) {
                                // Update
                                long time = (new GregorianCalendar()).getTimeInMillis();
                                String idsRedakt = "", idsVerant = "", idsUnter = "", idsFelder = "";
                                for (int i = 0; i < knoten.kinder.size(); i++) {
                                        idsUnter += knoten.kinder.get(i).wert + ";";
                                }
                                if (idsUnter.length() > 1) {
                                        idsUnter.substring(0, idsUnter.length() - 1);
                                }
                                if (ebene.getVerantwortlich() != null) {
                                        for (int i = 0; i < ebene.getVerantwortlich().length; i++) {
                                                idsVerant += ebene.getVerantwortlich()[i].getId() + ";";
                                        }
                                }
                                if(idsVerant.length() > 1) {
                                        idsVerant.substring(0, idsVerant.length() - 1);
                                }
                                if (ebene.getRedakteur() != null) {
                                        for (int i = 0; i < ebene.getRedakteur().length; i++) {
                                                idsRedakt += ebene.getRedakteur()[i].getId() + ";";
                                        }
                                }
                                if (idsRedakt.length() > 1) {
                                        idsRedakt.substring(0, idsRedakt.length() - 1);
                                }
                                if (ebene.getFeld() != null) {
                                        for (int i = 0; i < ebene.getFeld().length; i++) {
                                                idsFelder += ebene.getFeld()[i].getId() + ";";
                                                felderRepository.saveFeld(new Feld(ebene.getFeld()[i].getId(),
                                                                                                                        ebene.getFeld()[i].getTyp(),
                                                                                                                        ebene.getFeld()[i].getName(),
                                                                                                                        ebene.getFeld()[i].isDez2relevant(),
                                                                                                                        time,
                                                                                                                        ebene.getFeld()[i].getWert()));
                                        }
                                }
                                if (idsFelder.length() > 1) {
                                        idsFelder.substring(0, idsFelder.length() - 1);
                                }
                                ebene = new Ebene(ebene.getId(),
	                                                ebene.getName(),
	                                                ebene.getProsaId(),
	                                                time,
	                                                ebene.getRedaktionszustand(),
	                                                idsVerant,
	                                                idsRedakt,
	                                                idsUnter,
	                                                idsFelder);
                                geaenderteEbenen.add(ebene);
                        }
                }
        }

        private TreeNode<Integer> parseBaumElement(String newBaum) {
                TreeNode<Integer> knoten = new TreeNode<Integer>();
                int idStart = newBaum.indexOf("\"addClass\":\"id");
                int noIdStart = newBaum.indexOf("\"addClass\":null");
                if ((noIdStart < idStart && noIdStart != -1) || idStart == -1) {
                        knoten.wert = -1; // ID parsen
                } else {
                        knoten.wert = Integer.parseInt(newBaum.substring(idStart + "\"addClass\":\"id".length(),
                                                                                                                                newBaum.indexOf("\"", idStart + "\"addClass\":\"id".length()))); // ID parsen
                }
                System.out.println("parseElement" + knoten.wert + " - " + newBaum);
                int c, start;
                start = newBaum.indexOf("\"children\":["); // Schauen ob Kindelemente
                if (start != -1) {
                        System.out.println("startstartstart");
                        newBaum = newBaum.substring(start + "\"children\":[".length(), newBaum.length());
                        for (int pos = 0; pos < newBaum.length();) { // Alle Kinder durchgehen
                                start = -1;
                                c = 0;
                                while (c == 0 && pos < newBaum.length()) { // Start finden
                                        if (newBaum.charAt(pos) == '{') {
                                                c++;
                                                start = pos;
                                                System.out.println("start:" + start);
                                        }
                                        pos++;
                                }
                                while (pos < newBaum.length() && c > 0) { // Ende finden
                                        System.out.println("pos:" + pos + " c:" + c);
                                        if (newBaum.charAt(pos) == '{') {
                                                c++;
                                        } else if (newBaum.charAt(pos) == '}') {
                                                c--;
                                        }
                                        pos++;
                                }
                                if (start != -1 && c == 0) { // Wenn Start und Ende gefunden, dann Substring und weitere rekursion
                                        knoten.kinder.add(parseBaumElement(newBaum.substring(start, pos)));
                                }
                        }
                }
                return knoten;
        }

        private static boolean rekursionVorhanden(TreeNode<Integer> kind, ArrayList<Integer> eltern) {
                for(int i=0; i<eltern.size(); i++) {
                        if(eltern.get(i).intValue() == kind.wert.intValue()) {
                                return true;
                        }
                }
                if(kind.kinder != null) {
                        ArrayList<Integer> elternNeu = new ArrayList<Integer>();
                        for(int i=0; i<eltern.size(); i++) {
                                elternNeu.add(eltern.get(i));
                        }
                        elternNeu.add(kind.wert);
                        for(int i=0; i<kind.kinder.size(); i++) {
                                if(rekursionVorhanden(kind.kinder.get(i), elternNeu)) {
                                        return true;
                                }
                        }
                }
                return false;
        }
}


class TreeNode<E> {
        ArrayList<TreeNode<E>> kinder;
        E wert;

        public TreeNode() {
                kinder = new ArrayList<TreeNode<E>>();
                wert = null;
        }

}
